frontend/pages/e/[uuid]/details.tsx (view raw)
1import moment from 'moment';
2import Button from '@material-ui/core/Button';
3import Box from '@material-ui/core/Box';
4import Link from '@material-ui/core/Link';
5import Paper from '@material-ui/core/Paper';
6import Divider from '@material-ui/core/Divider';
7import Container from '@material-ui/core/Container';
8import TextField from '@material-ui/core/TextField';
9import Typography from '@material-ui/core/Typography';
10import {makeStyles} from '@material-ui/core/styles';
11import {DatePicker} from '@material-ui/pickers';
12import {PropsWithChildren, useState} from 'react';
13import {useTranslation} from 'react-i18next';
14import ShareEvent from '../../../containers/ShareEvent';
15import useEventStore from '../../../stores/useEventStore';
16import useToastStore from '../../../stores/useToastStore';
17import useSettings from '../../../hooks/useSettings';
18import EventLayout, {TabComponent} from '../../../layouts/Event';
19import {
20 EventByUuidDocument,
21 useUpdateEventMutation,
22} from '../../../generated/graphql';
23import pageUtils from '../../../lib/pageUtils';
24
25interface Props {
26 eventUUID: string;
27 announcement?: string;
28}
29
30const Page = (props: PropsWithChildren<Props>) => {
31 return <EventLayout {...props} Tab={DetailsTab} />;
32};
33
34const DetailsTab: TabComponent = ({}) => {
35 const {t} = useTranslation();
36 const settings = useSettings();
37 const [updateEvent] = useUpdateEventMutation();
38 const addToast = useToastStore(s => s.addToast);
39 const setEventUpdate = useEventStore(s => s.setEventUpdate);
40 const event = useEventStore(s => s.event);
41 const [isEditing, setIsEditing] = useState(false);
42 const idPrefix = isEditing ? 'EditEvent' : 'Event';
43 const classes = useStyles();
44
45 const onSave = async e => {
46 try {
47 const {uuid, ...data} = event;
48 const {id, travels, waitingPassengers, __typename, ...input} = data;
49 await updateEvent({
50 variables: {uuid, eventUpdate: input},
51 refetchQueries: ['eventByUUID'],
52 });
53 setIsEditing(false);
54 } catch (error) {
55 console.error(error);
56 addToast(t('event.errors.cant_update'));
57 }
58 };
59
60 const modifyButton = isEditing ? (
61 <Button
62 variant="contained"
63 color="primary"
64 className={classes.modify}
65 onClick={onSave}
66 >
67 {t('event.details.save')}
68 </Button>
69 ) : (
70 <Button
71 variant="text"
72 color="primary"
73 className={classes.modify}
74 onClick={() => setIsEditing(true)}
75 >
76 {t('event.details.modify')}
77 </Button>
78 );
79
80 if (!event) return null;
81
82 return (
83 <Box className={classes.root}>
84 <Container maxWidth="sm" className={classes.card}>
85 <Paper className={classes.paper}>
86 {modifyButton}
87 <div className={classes.section}>
88 <Typography variant="h6">{t('event.fields.name')}</Typography>
89 {isEditing ? (
90 <TextField
91 fullWidth
92 value={event.name}
93 onChange={e => setEventUpdate({name: e.target.value})}
94 name="name"
95 id="EditEventName"
96 />
97 ) : (
98 <Typography variant="body1" id={`${idPrefix}Name`}>
99 {event.name ?? t('event.fields.empty')}
100 </Typography>
101 )}
102 </div>
103 <div className={classes.section}>
104 <Typography variant="h6">{t('event.fields.date')}</Typography>
105 {isEditing ? (
106 <DatePicker
107 fullWidth
108 placeholder={t('event.fields.date_placeholder')}
109 value={event.date}
110 onChange={date =>
111 setEventUpdate({
112 date: !date ? null : moment(date).format('YYYY-MM-DD'),
113 })
114 }
115 format="DD/MM/YYYY"
116 cancelLabel={t('generic.cancel')}
117 clearable
118 clearLabel={t('generic.clear')}
119 id={`${idPrefix}Date`}
120 />
121 ) : (
122 <Typography variant="body1" id={`${idPrefix}Date`}>
123 {event.date
124 ? moment(event.date).format('DD/MM/YYYY')
125 : t('event.fields.empty')}
126 </Typography>
127 )}
128 </div>
129 <div className={classes.section}>
130 <Typography variant="h6">{t('event.fields.address')}</Typography>
131 {isEditing ? (
132 <TextField
133 fullWidth
134 multiline
135 rowsMax={4}
136 inputProps={{maxLength: 250}}
137 helperText={`${event.address?.length ?? 0}/250`}
138 defaultValue={event.address}
139 value={event.address}
140 onChange={e => setEventUpdate({address: e.target.value})}
141 id={`${idPrefix}Address`}
142 name="address"
143 />
144 ) : (
145 <Typography variant="body1" id={`${idPrefix}Address`}>
146 {event.address ? (
147 <Link
148 target="_blank"
149 rel="noreferrer"
150 href={`https://www.google.com/maps/search/?api=1&query=${encodeURIComponent(
151 event.address
152 )}`}
153 onClick={e => e.preventDefault}
154 >
155 {event.address}
156 </Link>
157 ) : (
158 t('event.fields.empty')
159 )}
160 </Typography>
161 )}
162 </div>
163 <div className={classes.section}>
164 <Typography variant="h6">
165 {t('event.fields.description')}
166 </Typography>
167 {isEditing ? (
168 <TextField
169 fullWidth
170 multiline
171 rowsMax={4}
172 inputProps={{maxLength: 250}}
173 helperText={`${event.description?.length || 0}/250`}
174 defaultValue={event.description}
175 value={event.description || ''}
176 onChange={e => setEventUpdate({description: e.target.value})}
177 id={`${idPrefix}Description`}
178 name="description"
179 />
180 ) : (
181 <Typography variant="body1" id={`${idPrefix}Description`}>
182 {event.description ?? t('event.fields.empty')}
183 </Typography>
184 )}
185 </div>
186 <Typography variant="h6">{t('event.fields.link')}</Typography>
187 <Typography>{t('event.fields.link_desc')}</Typography>
188 <Box py={4} justifyContent="center" display="flex">
189 <ShareEvent
190 title={`Caroster ${event.name}`}
191 url={`${window.location.href}`}
192 />{' '}
193 </Box>
194 <Divider variant="middle" />
195 <Box pt={2} justifyContent="center" display="flex">
196 <Link href={settings?.about_link} target="_blank" rel="noopener">
197 {t('event.details.aboutCaroster')}
198 </Link>
199 </Box>
200 </Paper>
201 </Container>
202 </Box>
203 );
204};
205
206const useStyles = makeStyles(theme => ({
207 root: {
208 position: 'relative',
209 paddingLeft: '80px',
210
211 [theme.breakpoints.down('sm')]: {
212 paddingLeft: 0,
213 paddingBottom: '80px',
214 },
215 },
216 paper: {
217 position: 'relative',
218 padding: theme.spacing(2),
219 },
220 card: {
221 marginTop: theme.spacing(6),
222 },
223 modify: {
224 position: 'absolute',
225 right: theme.spacing(2),
226 },
227 section: {
228 marginBottom: theme.spacing(2),
229 width: '540px',
230 maxWidth: '100%',
231 paddingX: theme.spacing(2),
232 },
233 map: {
234 marginTop: theme.spacing(4),
235 },
236 seeOnGMapButton: {
237 marginLeft: theme.spacing(2),
238 },
239}));
240
241export const getServerSideProps = pageUtils.getServerSideProps(
242 async (context, apolloClient) => {
243 const {uuid} = context.query;
244 const {host = ''} = context.req.headers;
245 let event = null;
246
247 // Fetch event
248 try {
249 const {data} = await apolloClient.query({
250 query: EventByUuidDocument,
251 variables: {uuid},
252 });
253 event = data?.eventByUUID?.data;
254 } catch (error) {
255 return {
256 notFound: true,
257 };
258 }
259
260 return {
261 props: {
262 eventUUID: uuid,
263 metas: {
264 title: event?.attributes?.name || '',
265 url: `https://${host}${context.resolvedUrl}`,
266 },
267 },
268 };
269 }
270);
271export default Page;